#include <linux/fs.h>
#include <sys/types.h>

/**
 * end 变量是 由链接器自动生成的符号
 * 用于标记内核映像的结束地址（即未初始化数据段 BSS 的末尾地址）
 * 标识内核代码和数据段之后的可用物理内存起始位置。
 * 该地址之后的内存通常用于动态分配（如页表、内核堆等）
 * 其定义是通过链接脚本（Linker Script）隐式生成的
 */
extern int end;

/**
 * 因此缓冲块起始位置就是内核镜像结束位置
 */
struct buffer_head * start_buffer = (struct buffer_head *) &end;
struct buffer_head * hash_table[NR_HASH];
static struct buffer_head * free_list;
int NR_BUFFERS = 0;

int sys_sync(void)
{
    return 0; /* //TODO */
}

/**
 * 高速缓存区初始化
 * buffer_end指向的就是高速缓冲区的结束地址处
 * 根据整体内存管理的大小，指向不同，具体可以参考 main.c 中的逻辑
 */
void buffer_init(long buffer_end)
{
    /* h是缓冲区起始位置，初始值就是内核镜像结束位置 */
    struct buffer_head *h = start_buffer;
    void * b; /* 缓冲区结束位置 */
    /* 因此高速缓冲区使用的是 h - b 之间的这段内存 */
	int i;

    if (buffer_end == (1<<20)) {
        /**
		 * 如果缓冲区结束地址是1M（则内存大小<6M），则b取640K 
		 *  缓冲区结束取640K是因为：640KB-1M之间的地址，会用作各种显示/BIOS等缓冲区
		 * 因此高速缓冲区从内核镜像结束到-640K之间，1M-6M会用作主内存进行内存管理和分配
		 * */
        b = (void *) (640*1024); 
    } else {
        /* 如果缓冲区结束地址大于1M，则b直接指向缓存区结束地址 */
        b = (void *) buffer_end;
    }

    /**
	 * h+1 指向的是高速缓冲区按照 buffer_head 结构之后的缓冲区第一个结构体末尾地址
	 * 如果 b 指向的地址大于一个 buffer_head 结构体，则继续初始化
	 * 初始化的过程实际上是在高速缓冲区从后往前，每次分块1KB，对应的是
	 * 		从前往后 buffer_head 分配一个结构
     * 因此从头部开始往后，分配的是高速缓冲区结构体buffer_head
     * 从尾部开始往前，分配的是高速缓冲区块block
     * buffer_head结构和block是一一对应的
     * 
     * BLOCK_SIZE 是 1K = 1024 字节
	 */
    while ( (b -= BLOCK_SIZE) >= ((void *) (h + 1)) ) {
        h->b_dev = 0;       /* 初始时设备号为0，表示未用 */
        h->b_dirt = 0;      /* 初始化为0，表示干净的 */
        h->b_count = 0;     /* 引用计数，初始化为0，表示无人使用 */
        h->b_lock = 0;      /* 是否锁定，这个字段用于同步 */
        h->b_uptodate = 0;  /* 是否更新到最新，和 b_dirty 的区别是什么呢？ */
        h->b_wait = NULL;   /* 在该缓冲区块等待的进程 */
        h->b_next = NULL;
        h->b_prev = NULL;
        h->b_data = (char *)b; /* 注意高速缓冲块与高速缓冲块结构体之间的对应关系 */
        h->b_prev_free = h - 1;
        h->b_next_free = h + 1; /* 注意前后管理块的连接 */
        h++;
        NR_BUFFERS++;
        /**
		 * 这里说明了，如果高速缓冲区>1M的部分已经分配完，那么继续分配内核镜像结束到640K
		 * 之间的部分，真是一点都不浪费啊
		 * */
        if (b == (void *) 0x100000) {
			b = (void *) 0xA0000;
        }
    }
    h--; /* 调整h指向最后一个高速缓冲管理块结构 */
    free_list = start_buffer; /* 初始状态时，都是空闲的 */
    free_list->b_prev_free = h; /* 循环指针，调整头部指向尾部 */
    h->b_next_free = free_list; /* 头尾相接 */

    /**
	 * 用hash数组应该是为了更快的找出某个块的信息
	 * 后面再看看具体是什么使用的，为何不直接使用 start_buffer + offset_i 的方式
     *  回答上述问题：因为hash表中每一项也是采用链表组织的形式，并非是一一对应
	 * */
    for (i = 0; i < NR_HASH; i++) {
        hash_table[i] = NULL;
    }
}